<?php
declare(strict_types=1);

namespace ServerApp\lib\server\tcp;

use ServerApp\lib\core\STcpServer;
use ServerApp\lib\process\SGlobal;
use ServerApp\lib\process\SProcess;
use ServerApp\lib\server\model\swoole\DeviceList;
use ServerApp\lib\server\model\swoole\FdList;
use Swoole\Server;
use swoole_redis;
use Throwable;
use WebApp\lib\config\Config;
use WebApp\lib\EventCenter;

/**
 * Class Tcp
 */
class Tcp extends STcpServer
{
    const LONG_TASK = "long_task";

    /**
     * @var null|ITcp
     */
    private $func = null;

    /**
     * Tcp constructor.
     * @param ITcp $func1
     */
    public function __construct(ITcp $func1)
    {
        $this->func = $func1;
        parent::__construct();
    }

    /**
     * @return string
     */
    protected function getMaster(): string
    {
        return $this->func->getName() . "_m1";
    }

    /**
     * @return string
     */
    protected function getManager(): string
    {
        return $this->func->getName() . "_m2";
    }

    /**
     * @return string
     */
    protected function getWorker(): string
    {
        return $this->func->getName() . "_w";
    }

    /**
     * @return int
     */
    public function getPort(): int
    {
        return PORT;
    }

    /**
     * @return string
     */
    protected function getBind(): string
    {
        return '0.0.0.0';
    }

    /**
     * @return array
     */
    protected function getSetting(): array
    {
        return array_merge(
            Config::getConf('server.setting'),
            Config::getConf('server.children.' . SERVER_NAME)
        );
    }

    public function intervalHeartBeat(){
        tracker(I, __FUNCTION__);

    }

    protected function Start(): void
    {
        tracker(I, "master start");
    }

    protected function ManagerStart(): void
    {
        tracker(I, "manager start");
    }

    public $key = '';

    /**
     * @param $server
     * @param $worker_id
     */
    protected function WorkerStart(Server $server, $worker_id): void
    {
        SGlobal::getInstance()->server = $server;
        $this->func->workerStart();

        tracker(I, "worker $worker_id start");
        if($worker_id === 0){
            $this->createLongTask();
        }
        $this->registerMsgQ();

//        $this->checkSwitch();

        $this->subscribeEvent();
    }

    private function subscribeEvent()
    {
        tracker(I, 'sub login');
        EventCenter::getInstance()->subscribe('login', function ($fd, $device_id) {
            $fd = $fd . '';
            $device_id = $device_id . '';

            $res = FdList::getInstance()->setRow($fd, [
                FdList::DEVICE=> $device_id . ''
            ]);
            if (!$res) {
                tracker(E, "add FdList $fd: $device_id = " . var_export($res, true));
            }

            $res = DeviceList::getInstance()->setRow($device_id, [
                DeviceList::FD=> intval($fd)
            ]);
            if (!$res) {
                tracker(E, "add DeviceList $device_id: $fd = " . var_export($res, true));
            }
        });
    }

//    private function checkSwitch()
//    {
//        $tmp = new stdClass();
//        $tmp->func = function() use($tmp){
//            while ($data = RedisPool::getInstance()->exec(function(Redis $client){
//                return $client->rPop(SERVER_NAME);
//            })) {
//                list($str, $time) = explode("\v", $data);
//                if (time() > $time) {
//                    continue;
//                }
//                list($to, $mid, $data) = explode(Udp::SPLIT, $str);
//                $this->func->udpReceiver($to, $mid, $data);
//            }
//            swoole_timer_after(1000, $tmp->func);
//        };
//        ($tmp->func)();
//    }

    private function registerMsgQ()
    {
        try{
            $methods = $this->func->getMsgQArr();
            if ($methods) {
                $host = Config::getConf('redis.ip');
                $port = Config::getConf('redis.port');

                $client = new swoole_redis;
                $client->on('message', function (swoole_redis $client, $result) use(&$methods) {
                    if ($result[0] === 'message') {
                        $methods[$result[1]]($result[2]);
                    }
                });
                $client->connect($host, $port, function (swoole_redis $client, $result) use(&$methods) {
                    $client->subscribe(...array_keys($methods));
                });
            }
        } catch (Throwable $e) {
            if (isset($client)) {
                tracker(E, join('@', [
                    $e->getMessage(),
                    $e->getCode(),
                    $e->getFile(),
                    $e->getLine()
                ]));
                tracker(I, 'redis close');
                $client->close();
                unset($client);
                $this->registerMsgQ();
            }
        }
    }

    private function createLongTask(){
        $arr = $this->func->getLongTasks();
        if (!$arr) {
            return;
        }

        (new SProcess(function(){
            $arr = $this->func->getLongTasks();
            foreach ($arr as $interval_time => $task) {
                $task();
                swoole_timer_tick($interval_time * 1000, function() use($task) {
                    $task();
                });
            }
        }))->start();
    }

    public function __destruct()
    {
        foreach (SGlobal::getInstance()->child_process_pids as $pid => $noUse) {
            SProcess::kill($pid, $signo = SIGTERM);
        }
//
//        if($this->server && $this->server->connection_list()){
//            foreach ($this->server->connection_list() as $fd){
//                $row = FdList::getInstance()->get($fd . '');
//                $deviceId = $row[FdList::DEVICE];
//                Online::getInstance()->hDel($deviceId . '');
//            }
//        }
    }

    /**
     * @param $server
     * @param $worker_id
     */
    protected function WorkerStop(Server $server, $worker_id): void
    {
        tracker(I, "worker $worker_id stop");
        if($worker_id === 0){
            foreach (SGlobal::getInstance()->child_process_pids as $pid => $noUse) {
                SProcess::kill($pid, $signo = SIGTERM);
            }
        }
//        $GLOBALS['process']->write('stop');
    }

    /**
     * @param $server
     * @param $fd
     */
    protected function Close(Server $server, $fd): void
    {
        tracker(I, "Client{$fd} close");
        $row = FdList::getInstance()->getRow($fd);
        if(!$row){
            return;
        }
        $deviceId = $row[FdList::DEVICE];
        FdList::getInstance()->delRow($fd);
        DeviceList::getInstance()->delRow($deviceId);

        $this->func->fdClose($fd, $deviceId);
    }

    /**
     * @param $server
     * @param $fd
     */
    protected function Connect(Server $server, $fd): void
    {
        $this->func->fdConnect($fd);
    }

    /**
     * @param \Swoole\Server $server
     * @param $fd
     * @param $from_id
     * @param $data
     */
    protected function Receive(Server $server, $fd, $from_id, $data): void
    {
        $this->func->receive($server, $fd, $from_id, $data, $this);
    }

    /**
     * @return mixed
     */
    protected function getService()
    {
        return $this->func->getServiceInstance();
    }
}


//        (new SProcess(function(){
//            $methods = $this->func->getMsgQArr();
//            if ($methods) {
//                $host = Config::getConf('redis.ip');
//                $port = Config::getConf('redis.port');
//                $redis = new \Redis();
//                ini_set('default_socket_timeout', '-1');  //不超时
//                $redis->connect($host, $port);
//            }
//            try{
//                if (isset($redis)) {
//                    tracker(I, 'redis connect');
//                    $redis->subscribe(array_keys($methods), function($instance, $channelName, $message) use(&$methods){
//                        unset($instance);
//                        $methods[$channelName]($message);
//                    });
//                }
//            } catch (Throwable $e) {
//                if (isset($redis)) {
//                    tracker(E, join('@', [
//                        $e->getMessage(),
//                        $e->getCode(),
//                        $e->getFile(),
//                        $e->getLine()
//                    ]));
//                    tracker(I, 'redis close');
//                    $redis->close();
//                    $this->registerMsgQ();
//                }
//            }
//        }, false, 0, 'registerMsgQ'))->start();